package com.gowants.pems.aspect;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import javax.servlet.http.HttpServletRequest;

import org.apache.shiro.SecurityUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.gowants.pems.mapper.SysLogMapper;
import com.gowants.pems.model.SysUser;
import com.gowants.pems.model.SysLog;
import com.gowants.pems.util.DateUtil;
import com.gowants.pems.util.Logger;
import com.gowants.pems.util.NetworkUtil;

/**
 * 日志打印及保存切面 (使用注解类@TsmsAopLog实现日志记录)
 * 
 * @author GJR
 *
 */
@Aspect
@Component
@Order(0)
public class ScmsLogAspect {

	private static final Logger logger = Logger.getLogger(ScmsLogAspect.class);

	@Autowired
	private SysLogMapper sysLogMapper;

	/**
	 * 
	 * <获取@TsmsAopLog 注解上信息>
	 * 
	 * @param joinPoint
	 * @return
	 * @throws Exception
	 * @remake create by GJR 2019年5月9日 下午5:35:20
	 */
	@SuppressWarnings("rawtypes")
	public static Map<String, Object> getControllerAnnotationValue(JoinPoint joinPoint) throws Exception {

		String targetName = joinPoint.getTarget().getClass().getName();
		String methodName = joinPoint.getSignature().getName();
		Object[] arguments = joinPoint.getArgs();
		Class targetClass = Class.forName(targetName);
		Method[] methods = targetClass.getMethods();
		Map<String, Object> map = new HashMap<>();

		for (Method method : methods) {
			if (method.getName().equals(methodName)) {
				Class[] classes = method.getParameterTypes();
				if (classes.length == arguments.length) {
					// 取入参数据
					ScmsAopLog tsmsAopLog = method.getAnnotation(ScmsAopLog.class);
					if (null == tsmsAopLog) {
						continue;
					}

					String description = method.getAnnotation(ScmsAopLog.class).description();
					OperType opType = method.getAnnotation(ScmsAopLog.class).opType();
					Importance importance = method.getAnnotation(ScmsAopLog.class).importance();
					map.put("description", description);
					map.put("opType", opType);
					map.put("importance", importance);
					break;
				}
			}
		}
		return map;
	}

	/**
	 * 
	 * <定义一个切入点> ("execution(public * com.gowants.tsms.service.*Service.*(..))") 第一个
	 * * 代表任意修饰符及任意返回值. 第二个 * 任意包名 第三个 * 代表任意方法. 第四个 * 定义在web包或者子包 第五个 * 任意方法 ..
	 * 匹配任意数量的参数.
	 * 
	 * @remake create by GJR 2019年5月9日 下午1:55:30
	 */
	@Pointcut("execution(public * com.gowants.pems.controller.*Controller.*(..))")
	public void pointCut() {

	}

	/**
	 * <异常日志>
	 *
	 * @param
	 * @return
	 * @remake create by GJR 2019/6/25 12:01
	 */
	@AfterThrowing(pointcut = "pointCut()", throwing = "throwable")
	public void afterThrowing(JoinPoint joinPoint, Throwable throwable) {
		HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
				.getRequest();
		String methodName = joinPoint.getSignature().getName();
		logger.info("Method Name : [" + methodName + "] ---> 异常信息 \n\r" + throwable.getMessage());

		try {
			// 获取注解参数
			Map<String, Object> values = getControllerAnnotationValue(joinPoint);
			values.put("result", "0");
			values.put("exceptionInfo", throwable.getMessage());
			if (!values.isEmpty()) {
				this.saveLogger(values, request);
			}
		} catch (Throwable te) {
			logger.error(te.getMessage(), te);
			throw new RuntimeException(te.getMessage());
		}
	}

	/**
	 * <日志环绕>
	 *
	 * @param
	 * @return
	 * @remake create by GJR 2019/6/25 12:00
	 */
	@Around("pointCut()")
	public Object around(ProceedingJoinPoint joinPoint) {
		HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
				.getRequest();

		String methodName = joinPoint.getSignature().getName();

		try {
			logger.info(
					"Method Name : [" + methodName + "] begin ---> request[" + request.getParameter("request") + "]");

			long startTimeMillis = System.currentTimeMillis();
			// 调用 proceed() 方法才会真正的执行实际被代理的方法
			Object result = joinPoint.proceed();
			result = null == result ? new Object() : result;

			long execTimeMillis = System.currentTimeMillis() - startTimeMillis;

			logger.info("Method Name : [" + methodName + "] end " + execTimeMillis + "ms ---> result ["
					+ result.toString() + "]");
			// 获取注解参数
			Map<String, Object> values = getControllerAnnotationValue(joinPoint);

			// 添加注解的方法需要记录日志表
			if (!values.isEmpty()) {
				values.put("result", "1");
				this.saveLogger(values, request);

			}

			return result;

		} catch (Throwable te) {
			// te.printStackTrace();
			logger.error(te.getMessage(), te);
			throw new RuntimeException(te.getMessage());
		}
	}

	/**
	 * 
	 * <保存日志>
	 * 
	 * @return
	 * @remake create by GJR 2019年5月9日 下午5:44:14
	 */
	private int saveLogger(Map<String, Object> values, HttpServletRequest request) {
		try {
			 SysUser user = (SysUser) SecurityUtils.getSubject().getPrincipal();
			SysLog record = new SysLog();
			String exceptionInfo = (String) values.get("exceptionInfo");
			String result = (String) values.get("result");
			String description = (String) values.get("description");
			OperType opType = (OperType) values.get("opType");
			Importance importance = (Importance) values.get("importance");
			String remarks = result.equals("1") ? "请求成功" : "请求失败; 原因：" + exceptionInfo;
			if (!"1".equals(result)) {
				logger.error("错误信息:" + exceptionInfo);
			}
			String ip = NetworkUtil.getIpAddress(request);
			remarks += "(ip:" + ip + ")";
			record.setCreateTime(DateUtil.getDate());
			record.setRemarks(remarks);
			record.setType(opType.getNumber());
			record.setImportance(importance.getNumebr());
			record.setId(UUID.randomUUID().toString());
			if (null != user) {
				record.setCreateUid(user.getId());
				logger.info("用户:" + user.getName() + "  " + user.getName() + ", 执行了" + opType.getValue() + ", 需要记录"
						+ importance.getValue() + "日志");
			}

			record.setName(description);
			sysLogMapper.save(record);
			return 0;
		} catch (Throwable te) {
			// te.printStackTrace();
			logger.error(te.getMessage(), te);
			throw new RuntimeException(te.getMessage());
		}

	}
}